"""自由使用的MySql工具!!  Freedam GUI界面模块"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
import tkinter.font as tkfont
import threading
import os
from src.controller.app_controller import app_controller
from src.mysql_password_manager import MySQLPasswordManager
from src.logger import logger

# 导入PIL库用于显示图片
from PIL import Image, ImageTk

class MySQLPassGUI:
    """自由使用的MySql工具!!  Freedam GUI界面类"""
    
    def __init__(self, root):
        """初始化GUI界面"""
        logger.info("MySQLPassGUI界面初始化")
        self.root = root
        self.root.title("mysqlpass - 自由使用的MySql工具!!  Freedam")
        self.root.geometry("900x600")
        self.root.minsize(800, 500)
        
        # 设置字体
        self.font = ('Microsoft YaHei', 10)
        
        # 初始化变量 - 必须在调用create_tool_bar()之前定义
        self.name_var = tk.StringVar()
        self.host_var = tk.StringVar()
        self.port_var = tk.StringVar()
        self.user_var = tk.StringVar()
        self.password_var = tk.StringVar()
        self.status_var = tk.StringVar(value="未连接")
        self.max_rows_var = tk.StringVar(value="1000")  # 默认最多显示1000行
        
        # 初始化密码管理器
        self.password_manager = MySQLPasswordManager()
        
        # 初始化UI组件引用
        self.db_combobox = None
        self.query_text = None  # 保留向后兼容性
        self.result_tree = None
        
        # 查询编辑器列表，用于支持多个查询
        self.query_editors = []
        self.current_query_index = -1
        self.query_editor_frames = []  # 存储每个编辑器的框架
        
        # 查询页签和结果页签的映射关系
        self.query_result_map = []  # 存储每个查询页签对应的结果页签索引列表
        
        # 查询编辑器右键菜单相关
        self.query_editor_context_menu = None
        self.active_query_editor = None
        
        # 当前选中的连接和数据库
        self.selected_connection = None
        self.selected_database = None
        
        # 状态变量
        self.is_loading = False
        
        # SQL保留字列表（常见的MySQL关键字）
        self.sql_keywords = {
            'SELECT', 'FROM', 'WHERE', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP',
            'TABLE', 'DATABASE', 'INDEX', 'VIEW', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER',
            'ON', 'AND', 'OR', 'NOT', 'IN', 'LIKE', 'BETWEEN', 'ORDER', 'BY', 'GROUP', 'HAVING',
            'LIMIT', 'OFFSET', 'AS', 'DISTINCT', 'NULL', 'IS', 'EXISTS', 'UNION', 'ALL', 'ANY',
            'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'IF', 'THEN', 'ELSE', 'TRUE', 'FALSE',
            'ASC', 'DESC', 'WITH', 'USE', 'GRANT', 'REVOKE', 'COMMIT', 'ROLLBACK', 'BEGIN',
            'TRANSACTION', 'LOCK', 'UNLOCK', 'FOR', 'UPDATE', 'NOW', 'CURRENT_DATE', 'CURRENT_TIME'
        }
        
        # 创建主框架
        self.main_frame = ttk.Frame(self.root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建菜单栏
        self.create_menu_bar()
        
        # 创建工具栏 - 现在可以安全调用，因为max_rows_var已经定义
        self.create_tool_bar()
        
        # 创建主布局
        self.create_main_layout()
        
        # 刷新连接列表
        self.refresh_connections()
    
    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="新建连接", command=self.create_new_connection)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        menubar.add_cascade(label="文件", menu=file_menu)
        
        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        edit_menu.add_command(label="修改连接", command=self.edit_connection)
        edit_menu.add_command(label="删除连接", command=self.delete_connection)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        
        # 视图菜单
        view_menu = tk.Menu(menubar, tearoff=0)
        # 字体子菜单
        font_menu = tk.Menu(view_menu, tearoff=0)
        font_menu.add_command(label="设置字体", command=self.show_font_dialog)
        view_menu.add_cascade(label="字体", menu=font_menu)
        menubar.add_cascade(label="视图", menu=view_menu)
        
        # 优化建议菜单 - 移到文件右侧
        suggestion_menu = tk.Menu(menubar, tearoff=0)
        suggestion_menu.add_command(label="提交优化建议", command=self.show_suggestion_dialog)
        menubar.add_cascade(label="优化建议", menu=suggestion_menu)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(label="关于", command=self.show_about)
        menubar.add_cascade(label="帮助", menu=help_menu)
        
        # 检查更新菜单
        update_menu = tk.Menu(menubar, tearoff=0)
        update_menu.add_command(label="检查更新", command=self.check_for_updates)
        menubar.add_cascade(label="检查更新", menu=update_menu)
        
        # 自愿打赏菜单
        donation_menu = tk.Menu(menubar, tearoff=0)
        donation_menu.add_command(label="自愿打赏", command=self.show_donation_dialog)
        menubar.add_cascade(label="自愿打赏", menu=donation_menu)
        
        self.root.config(menu=menubar)
    
    def create_tool_bar(self):
        """创建工具栏"""
        toolbar = ttk.Frame(self.main_frame)
        toolbar.pack(fill=tk.X, padx=5, pady=5)
        
        # 左侧按钮组
        left_buttons_frame = ttk.Frame(toolbar)
        left_buttons_frame.pack(side=tk.LEFT)
        
        # 新建连接按钮
        self.new_conn_btn = ttk.Button(left_buttons_frame, text="新建连接", command=self.create_new_connection)
        self.new_conn_btn.pack(side=tk.LEFT, padx=2)
        
        # 编辑连接按钮
        self.edit_conn_btn = ttk.Button(left_buttons_frame, text="编辑连接", command=self.edit_connection)
        self.edit_conn_btn.pack(side=tk.LEFT, padx=2)
        
        # 删除连接按钮
        self.del_conn_btn = ttk.Button(left_buttons_frame, text="删除连接", command=self.delete_connection)
        self.del_conn_btn.pack(side=tk.LEFT, padx=2)
        
        # 新建查询按钮
        self.new_query_btn = ttk.Button(left_buttons_frame, text="新建查询", command=self.new_query)
        self.new_query_btn.pack(side=tk.LEFT, padx=2)
        
        # 执行查询按钮
        self.execute_query_btn = ttk.Button(left_buttons_frame, text="执行查询", command=self.execute_query)
        self.execute_query_btn.pack(side=tk.LEFT, padx=2)
        
        # 导出结果按钮
        self.export_results_btn = ttk.Button(left_buttons_frame, text="导出结果", command=self.export_results)
        self.export_results_btn.pack(side=tk.LEFT, padx=2)
        
        # 右侧区域
        right_frame = ttk.Frame(toolbar)
        right_frame.pack(side=tk.RIGHT, padx=5)
        
        # 右侧最多行数输入框
        right_input_frame = ttk.Frame(right_frame)
        right_input_frame.pack(side=tk.LEFT, padx=5)
        
        ttk.Label(right_input_frame, text="最多行数:", font=self.font).pack(side=tk.LEFT, padx=2)
        self.max_rows_entry = ttk.Entry(right_input_frame, textvariable=self.max_rows_var, width=8, font=self.font)
        self.max_rows_entry.pack(side=tk.LEFT, padx=2)
        
        # 移除右侧按钮组中的检查更新、帮助和优化建议按钮
        # 优化建议功能已移到菜单栏文件右侧
    
    def show_font_dialog(self):
        """显示字体设置对话框"""
        # 创建字体设置对话框
        font_dialog = tk.Toplevel(self.root)
        font_dialog.title("设置字体")
        font_dialog.geometry("400x250")
        font_dialog.resizable(False, False)
        font_dialog.transient(self.root)
        font_dialog.grab_set()
        
        # 获取当前字体信息
        current_font = self.font
        if isinstance(current_font, tuple):
            current_family = current_font[0]
            current_size = current_font[1]
        else:
            current_family = "Microsoft YaHei"
            current_size = 10
        
        # 字体选择区域
        frame = ttk.Frame(font_dialog, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 字体名称
        ttk.Label(frame, text="字体名称:", font=self.font).grid(row=0, column=0, sticky=tk.W, pady=5)
        
        # 获取可用字体列表
        available_fonts = sorted(tkfont.families())
        family_var = tk.StringVar(value=current_family)
        family_combobox = ttk.Combobox(frame, textvariable=family_var, values=available_fonts, width=30, state="readonly")
        family_combobox.grid(row=0, column=1, sticky=tk.W, pady=5)
        
        # 字体大小
        ttk.Label(frame, text="字体大小:", font=self.font).grid(row=1, column=0, sticky=tk.W, pady=5)
        
        # 常用字体大小列表
        font_sizes = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24]
        size_var = tk.IntVar(value=current_size)
        size_combobox = ttk.Combobox(frame, textvariable=size_var, values=font_sizes, width=10, state="readonly")
        size_combobox.grid(row=1, column=1, sticky=tk.W, pady=5)
        
        # 预览区域
        ttk.Label(frame, text="预览:", font=self.font).grid(row=2, column=0, sticky=tk.W, pady=5)
        
        preview_text = "不想成为海贼王的男人不是真男人"
        preview_label = ttk.Label(frame, text=preview_text, font=(family_var.get(), size_var.get()), width=40, anchor=tk.W)
        preview_label.grid(row=2, column=1, sticky=tk.W, pady=5)
        
        # 当选择改变时更新预览
        def update_preview():
            preview_label.config(font=(family_var.get(), size_var.get()))
        
        family_combobox.bind("<<ComboboxSelected>>", lambda event: update_preview())
        size_combobox.bind("<<ComboboxSelected>>", lambda event: update_preview())
        
        # 按钮区域
        button_frame = ttk.Frame(font_dialog)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(button_frame, text="确定", command=lambda: self._apply_font_change(family_var.get(), size_var.get(), font_dialog)).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="取消", command=font_dialog.destroy).pack(side=tk.RIGHT, padx=5)
    
    def _apply_font_change(self, family, size, dialog):
        """应用字体更改到所有查询编辑器"""
        # 更新字体设置
        self.font = (family, size)
        
        # 应用到所有已存在的查询编辑器
        for query_editor in self.query_editors:
            query_editor.config(font=self.font)
        
        # 如果有当前查询文本引用，也更新
        if self.query_text:
            self.query_text.config(font=self.font)
        
        logger.info(f"字体已更改为: {family}, 大小: {size}")
        dialog.destroy()
    
    def create_main_layout(self):
        """创建主布局"""
        # 创建主分割窗格
        self.paned_window = ttk.PanedWindow(self.main_frame, orient=tk.HORIZONTAL)
        self.paned_window.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 左侧连接列表 - 进一步减小连接区域的大小
        self.left_frame = ttk.Frame(self.paned_window, width=200)
        self.paned_window.add(self.left_frame, weight=1)
        
        # 创建树状视图
        self.tree = ttk.Treeview(self.left_frame)
        self.tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 绑定树状视图事件
        self.tree.bind("<<TreeviewSelect>>", lambda event: self.on_tree_select())
        # 绑定展开事件，用于加载表
        self.tree.bind("<<TreeviewOpen>>", self.on_tree_open)
        
        # 创建右键菜单
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(label="编辑连接", command=self.edit_connection)
        self.context_menu.add_command(label="删除连接", command=self.delete_connection)
        self.context_menu.add_command(label="刷新连接", command=self.refresh_connections)
        
        # 绑定右键点击事件
        self.tree.bind("<Button-3>", self.show_context_menu)
        
        # 点击其他区域关闭右键菜单
        self.root.bind("<Button-1>", self.on_click_close_menu)
        
        # 为结果树准备右键菜单
        self.result_tree_context_menu = None
        self.active_result_tree = None
        
        # 绑定Ctrl+C到复制功能
        self.root.bind("<Control-c>", self._on_copy_shortcut)
        
        # 右侧详情面板 - 进一步增大详情面板的比例
        self.right_frame = ttk.Frame(self.paned_window, width=1500)
        self.paned_window.add(self.right_frame, weight=10)
        
        # 创建详情标签页
        self.notebook = ttk.Notebook(self.right_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 连接信息标签页
        self.info_tab = ttk.Frame(self.notebook)
        self.notebook.add(self.info_tab, text="连接信息")
        self.create_info_tab()
        
        # 查询标签页
        self.query_tab = ttk.Frame(self.notebook)
        self.notebook.add(self.query_tab, text="查询")
        self.create_query_tab()
    
    def create_info_tab(self):
        """创建连接信息标签页"""
        # 创建表单框架
        form_frame = ttk.LabelFrame(self.info_tab, text="连接详情")
        form_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 连接名称
        ttk.Label(form_frame, text="连接名称:", font=self.font).grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
        self.name_var = tk.StringVar()
        ttk.Label(form_frame, textvariable=self.name_var, font=self.font).grid(row=0, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 主机名
        ttk.Label(form_frame, text="主机名/IP:", font=self.font).grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
        self.host_var = tk.StringVar()
        ttk.Label(form_frame, textvariable=self.host_var, font=self.font).grid(row=1, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 端口
        ttk.Label(form_frame, text="端口:", font=self.font).grid(row=2, column=0, sticky=tk.W, padx=10, pady=5)
        self.port_var = tk.StringVar()
        ttk.Label(form_frame, textvariable=self.port_var, font=self.font).grid(row=2, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 用户名
        ttk.Label(form_frame, text="用户名:", font=self.font).grid(row=3, column=0, sticky=tk.W, padx=10, pady=5)
        self.user_var = tk.StringVar()
        ttk.Label(form_frame, textvariable=self.user_var, font=self.font).grid(row=3, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 密码
        ttk.Label(form_frame, text="密码:", font=self.font).grid(row=4, column=0, sticky=tk.W, padx=10, pady=5)
        self.password_var = tk.StringVar()
        ttk.Label(form_frame, textvariable=self.password_var, font=self.font).grid(row=4, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 连接状态
        ttk.Label(form_frame, text="连接状态:", font=self.font).grid(row=5, column=0, sticky=tk.W, padx=10, pady=5)
        self.status_var = tk.StringVar(value="未连接")
        ttk.Label(form_frame, textvariable=self.status_var, font=self.font).grid(row=5, column=1, sticky=tk.W, padx=10, pady=5)
        
        # 底部按钮
        btn_frame = ttk.Frame(form_frame)
        btn_frame.grid(row=6, column=0, columnspan=2, pady=10)
        
        ttk.Button(btn_frame, text="测试连接", command=self.test_connection).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="复制密码", command=self.copy_password).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="修改", command=self.edit_connection).pack(side=tk.LEFT, padx=5)
    
    def _on_paned_window_configure(self, event=None):
        """当分割窗格大小变化时，调整两个区域的面积比例"""
        # 只有在两个子窗口都添加后才执行
        if len(self.query_paned_window.panes()) == 2:
            # 获取分割窗格的总高度
            total_height = self.query_paned_window.winfo_height()
            # 设置分隔线位置，使查询区域占据2/3的高度，结果区域占据1/3的高度
            if total_height > 0:
                # 计算查询区域的高度 (2/3 的总高度)
                query_height = int(total_height * 2 / 3)
                self.query_paned_window.sashpos(0, query_height)
                
    def create_query_tab(self):
        """创建查询标签页，包含查询和结果的组合标签页"""
        # 创建主框架
        main_query_frame = ttk.Frame(self.query_tab)
        main_query_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 数据库选择
        db_frame = ttk.LabelFrame(main_query_frame, text="数据库")
        db_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(db_frame, text="", font=self.font).pack(side=tk.LEFT, padx=5, pady=5)
        self.db_combobox = ttk.Combobox(db_frame, font=self.font, width=30, state="disabled")
        self.db_combobox.pack(side=tk.LEFT, padx=5, pady=5)
        self.db_combobox.bind("<<ComboboxSelected>>", lambda event: self.on_database_select())
        
        # 查询进度条
        self.progress_frame = ttk.Frame(main_query_frame)
        self.progress_frame.pack(fill=tk.X, padx=10, pady=5)
        self.progress_bar = ttk.Progressbar(self.progress_frame, orient=tk.HORIZONTAL, length=100, mode='indeterminate')
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5)
        # 默认隐藏进度条
        self.progress_frame.pack_forget()
        
        # 创建组合标签页容器 - 这是主要变化点
        self.combined_notebook = ttk.Notebook(main_query_frame)
        self.combined_notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 绑定标签页切换事件
        self.combined_notebook.bind("<<NotebookTabChanged>>", self._on_combined_tab_changed)
        
        # 绑定标签页点击事件
        self.combined_notebook.bind("<Button-1>", self._on_combined_tab_click)
        
        # 初始化查询和结果计数器
        self.query_tab_count = 0
        self.result_tab_count = 0
        
        # 存储查询编辑器和结果树的列表，每个元素是一个元组 (查询编辑器, 结果树, 查询框架, 结果框架)
        self.query_result_pairs = []
        
        # 创建第一个组合标签页
        self.new_query()
    
    def _load_databases_for_connection(self, conn_name, conn_node):
        """在后台线程中加载数据库列表"""
        # 尝试获取数据库列表
        success, result = app_controller.get_databases(conn_name)
        
        # 在主线程中更新UI
        def update_ui():
            # 移除"加载中"节点
            for child in self.tree.get_children(conn_node):
                self.tree.delete(child)
            
            if success:
                # 添加数据库节点
                for db in result:
                    # 创建数据库节点
                    db_node = self.tree.insert(conn_node, 'end', text=db, values=('database', conn_name, db))
                    # 添加一个临时的空子节点，然后立即删除它，这样可以让数据库节点显示为可展开状态
                    tmp_node = self.tree.insert(db_node, 'end')
                    self.tree.delete(tmp_node)
            else:
                # 添加错误信息节点
                self.tree.insert(conn_node, 'end', text=f'无法加载数据库: {result}', values=('error',))
        
        # 使用after方法在主线程中更新UI
        self.root.after(0, update_ui)
        
    def _create_new_result_tab(self, title=None):
        """创建一个新的结果标签页 - 注意：在组合标签页结构中，这个方法被show_results直接使用"""
        # 在组合标签页结构中，我们不需要创建新的标签页
        # 而是直接使用当前组合标签页中的结果树
        # 返回None表示使用现有结果树
        return None, None
        
        # 使用Frame来容纳结果树和滚动条，确保水平滚动条正确工作
        tree_frame = ttk.Frame(result_tab)
        tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建自定义样式，设置浅灰色边框
        style = ttk.Style()
        
        try:
            # 针对Windows系统Treeview优化：采用更简洁有效的样式方案
            # 1. 配置表格主体样式
            style.configure("GrayTable.Treeview", 
                            font=self.font,
                            rowheight=25,
                            fieldbackground="white",
                            background="white",
                            bordercolor="#d9d9d9",
                            borderwidth=1,
                            relief="solid")
            
            # 2. 配置表格标题样式
            style.configure("GrayTable.Treeview.Heading",
                            font=(self.font[0], self.font[1], "bold"),
                            background="#f0f0f0",
                            bordercolor="#d9d9d9",
                            borderwidth=1,
                            relief="solid",
                            padding=[5, 3])
            
            # 3. 配置选中行样式
            style.map("GrayTable.Treeview",
                      background=[('selected', '#c9c9c9')],
                      foreground=[('selected', 'black')])
            
            # 4. 针对Windows系统特殊优化：使用ttk默认布局但调整样式
            # 这是解决Windows上相邻列竖线问题的关键方法
            # 移除之前的复杂布局定义，改用更简单有效的方式
            # 重新定义布局，确保单元格之间显示单条竖线
            # 在Windows上，简单的布局反而能更好地显示单条竖线
            style.layout("GrayTable.Treeview.Cell", [
                ("Treeitem.cell", {
                    "sticky": "nswe",
                    "children": [
                        ("Treeitem.text", {"sticky": "nswe"})
                    ]
                })
            ])
            
        except Exception as e:
            logger.warning(f"无法设置单元格边框: {str(e)}")
        
        # 创建树状视图用于显示结果，应用自定义样式
        result_tree = ttk.Treeview(tree_frame, style="GrayTable.Treeview")
        
        # 为结果树创建右键菜单
        self._create_result_tree_context_menu(result_tree)
        
        # 添加垂直滚动条
        y_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=result_tree.yview)
        y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        result_tree.config(yscrollcommand=y_scrollbar.set)
        
        # 添加水平滚动条
        x_scrollbar = ttk.Scrollbar(result_tab, orient=tk.HORIZONTAL, command=result_tree.xview)
        x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        result_tree.config(xscrollcommand=x_scrollbar.set)
        
        # 配置结果树的pack方式，确保它能正确响应滚动条
        result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 确保结果树列不会自动收缩，这样水平滚动条才能正常工作
        result_tree['show'] = 'headings'  # 只显示列标题和数据，不显示第一列
        
        # 添加标签页到笔记本
        tab_text = title if title else f"结果 {self.result_tab_count}"
        self.results_notebook.add(result_tab, text=tab_text)
        
        # 返回标签页和结果树对象
        return result_tab, result_tree
        
    def refresh_connections(self):
        """刷新连接列表"""
        logger.info("刷新连接列表")
        # 清空现有节点
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # 添加根节点
        root_node = self.tree.insert('', 'end', text='MySQL连接', open=True)
        
        # 添加连接节点
        connections = app_controller.list_connections()
        for conn in connections:
            # 为每个连接创建节点，但先不加载数据库
            conn_node = self.tree.insert(root_node, 'end', text=conn, values=(conn,))
            # 添加一个临时的"加载中"节点
            self.tree.insert(conn_node, 'end', text='加载中...', values=('loading',))
            
            # 在后台线程中加载数据库列表
            threading.Thread(target=self._load_databases_for_connection, args=(conn, conn_node), daemon=True).start()
    
    def on_click_close_menu(self, event=None):
        """点击其他区域关闭右键菜单"""
        try:
            self.context_menu.unpost()
        except:
            pass
            
    def show_context_menu(self, event):
        """显示右键菜单"""
        # 先关闭可能已打开的菜单
        self.on_click_close_menu()
        
        # 获取右键点击的项目
        item = self.tree.identify_row(event.y)
        if item:
            # 选中点击的项目
            self.tree.selection_set(item)
            
            # 获取项目值
            values = self.tree.item(item, "values")
            if values and values[0] not in ('loading', 'error'):
                # 为不同类型的节点显示不同的右键菜单
                if values[0] == 'table':
                    # 创建表节点的右键菜单
                    table_menu = tk.Menu(self.root, tearoff=0)
                    table_menu.add_command(label="查看表信息", command=self._view_table_info)
                    table_menu.add_command(label="导出表结构", command=self._export_table_structure)
                    table_menu.post(event.x_root, event.y_root)
                elif values[0] == 'database':
                    # 创建数据库节点的右键菜单
                    db_menu = tk.Menu(self.root, tearoff=0)
                    db_menu.add_command(label="加载表信息", command=self._load_tables_for_selected_database)
                    db_menu.post(event.x_root, event.y_root)
                else:
                    # 对连接节点显示原来的右键菜单
                    self.context_menu.post(event.x_root, event.y_root)
        
    def on_tree_open(self, event):
        """树状视图展开事件处理"""
        # 获取展开的项
        item = self.tree.identify_row(event.y)
        if not item:
            return
        
        values = self.tree.item(item, "values")
        if not values:
            return
        
        # 检查是否是数据库节点
        if values[0] == 'database':
            conn_name = values[1]
            db_name = values[2]
            
            logger.info(f"用户展开数据库节点: {db_name} (连接: {conn_name})")
            
            # 检查是否已经加载了表
            children = self.tree.get_children(item)
            if not children:
                # 添加一个临时的"加载中"节点
                self.tree.insert(item, 'end', text='加载表中...', values=('loading',))
                
                # 在后台线程中加载表列表
                logger.info(f"开始加载数据库 {db_name} 的表列表")
                threading.Thread(target=self._load_tables_for_database, 
                                args=(conn_name, db_name, item), 
                                daemon=True).start()
        else:
            logger.debug(f"展开非数据库节点: {self.tree.item(item, 'text')}")
                
    def _load_tables_for_database(self, conn_name, db_name, db_node):
        """在后台线程中加载数据库的表列表"""
        logger.info(f"后台线程加载数据库 {db_name} 的表列表 (连接: {conn_name})")
        # 尝试获取表列表
        success, result = app_controller.get_tables(conn_name, db_name)
        
        logger.info(f"表列表加载结果: {'成功' if success else '失败'}, 详情: {result}")
        
        # 在主线程中更新UI
        def update_ui():
            # 移除"加载中"节点
            for child in self.tree.get_children(db_node):
                self.tree.delete(child)
            
            if success:
                # 添加表节点
                table_count = len(result)
                logger.info(f"向UI添加 {table_count} 张表 (数据库: {db_name}, 连接: {conn_name})")
                for table in result:
                    self.tree.insert(db_node, 'end', text=table, values=('table', conn_name, db_name, table))
            else:
                # 添加错误信息节点
                logger.error(f"加载表失败，显示错误信息: {result}")
                self.tree.insert(db_node, 'end', text=f'无法加载表: {result}', values=('error',))
        
        # 使用after方法在主线程中更新UI
        self.root.after(0, update_ui)
    
    def on_tree_select(self):
        """树状视图选择事件处理"""
        selected_item = self.tree.selection()
        if not selected_item:
            return
        
        # 获取选中的项
        item = selected_item[0]
        values = self.tree.item(item, "values")
        
        if not values:
            return
        
        # 检查项的类型
        item_type = values[0]
        
        if item_type in ('loading', 'error'):
            # 忽略加载中和错误节点
            return
        
        if item_type == 'database':
            # 选中的是数据库节点
            conn_name = values[1]
            db_name = values[2]
            
            self.selected_connection = conn_name
            self.selected_database = db_name
            
            logger.info(f"用户选择数据库: {db_name} (连接: {conn_name})")
            # 更新详情面板和数据库下拉框
            conn_info = app_controller.get_connection(conn_name)
            if conn_info:
                self.name_var.set(conn_name)
                self.host_var.set(conn_info['host'])
                self.port_var.set(conn_info['port'])
                self.user_var.set(conn_info['username'])
                self.password_var.set(conn_info['password'])
                
                # 测试连接状态
                self._update_connection_status(conn_name)
        elif item_type == 'table':
            # 选中的是表节点
            conn_name = values[1]
            db_name = values[2]
            table_name = values[3]
            
            self.selected_connection = conn_name
            self.selected_database = db_name
            
            logger.info(f"用户选择表: {table_name} (数据库: {db_name}, 连接: {conn_name})")
            # 更新详情面板
            conn_info = app_controller.get_connection(conn_name)
            if conn_info:
                self.name_var.set(conn_name)
                self.host_var.set(conn_info['host'])
                self.port_var.set(conn_info['port'])
                self.user_var.set(conn_info['username'])
                self.password_var.set(conn_info['password'])
                
                # 测试连接状态
                self._update_connection_status(conn_name)
                
            # 更新数据库下拉框
            self._update_database_combobox(conn_name)
            self.db_combobox.set(db_name)
        else:
            # 选中的是连接节点
            conn_name = item_type
            
            self.selected_connection = conn_name
            self.selected_database = None
            
            # 更新详情面板
            conn_info = app_controller.get_connection(conn_name)
            if conn_info:
                self.name_var.set(conn_name)
                self.host_var.set(conn_info['host'])
                self.port_var.set(conn_info['port'])
                self.user_var.set(conn_info['username'])
                self.password_var.set(conn_info['password'])
                
                # 测试连接状态
                self._update_connection_status(conn_name)
                
            # 更新数据库下拉框
            self._update_database_combobox(conn_name)
    
    def _update_connection_status(self, conn_name):
        """更新连接状态"""
        # 在后台线程中测试连接
        threading.Thread(target=self._test_connection_async, args=(conn_name,), daemon=True).start()
        
    def _test_connection_async(self, conn_name):
        """异步测试连接"""
        logger.info(f"异步测试连接: {conn_name}")
        success, message = app_controller.test_connection(conn_name)
        
        # 在主线程中更新状态
        def update_status():
            if success:
                self.status_var.set("已连接")
                logger.info(f"连接状态更新: {conn_name} (已连接)")
            else:
                self.status_var.set(f"连接失败: {message}")
                logger.warning(f"连接状态更新: {conn_name} (连接失败: {message})")
        
        self.root.after(0, update_status)
        
    def _update_database_combobox(self, conn_name):
        """更新数据库下拉框"""
        # 在后台线程中获取数据库列表
        threading.Thread(target=self._load_databases_for_combobox, args=(conn_name,), daemon=True).start()
        
    def _load_databases_for_combobox(self, conn_name):
        """为下拉框加载数据库列表"""
        success, result = app_controller.get_databases(conn_name)
        
        # 在主线程中更新下拉框
        def update_combobox():
            if success:
                self.db_combobox['values'] = result
                self.db_combobox['state'] = 'readonly'
                if result:
                    self.db_combobox.current(0)
                    self.selected_database = result[0]
            else:
                self.db_combobox['values'] = []
                self.db_combobox['state'] = 'disabled'
                self.selected_database = None
        
        self.root.after(0, update_combobox)
    
    def create_new_connection(self):
        """创建新连接"""
        logger.info("用户点击创建新连接")
        # 创建对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("新建连接")
        dialog.geometry("450x350")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
        y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
        dialog.geometry(f'{width}x{height}+{x}+{y}')
        
        # 创建表单
        ttk.Label(dialog, text="连接名称:", font=self.font).grid(row=0, column=0, sticky=tk.W, padx=20, pady=10)
        name_entry = ttk.Entry(dialog, font=self.font, width=30)
        name_entry.grid(row=0, column=1, padx=10, pady=10)
        
        ttk.Label(dialog, text="主机名/IP:", font=self.font).grid(row=1, column=0, sticky=tk.W, padx=20, pady=10)
        host_entry = ttk.Entry(dialog, font=self.font, width=30)
        host_entry.insert(0, "localhost")
        host_entry.grid(row=1, column=1, padx=10, pady=10)
        
        ttk.Label(dialog, text="端口:", font=self.font).grid(row=2, column=0, sticky=tk.W, padx=20, pady=10)
        port_entry = ttk.Entry(dialog, font=self.font, width=30)
        port_entry.insert(0, "3306")
        port_entry.grid(row=2, column=1, padx=10, pady=10)
        
        ttk.Label(dialog, text="用户名:", font=self.font).grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)
        user_entry = ttk.Entry(dialog, font=self.font, width=30)
        user_entry.grid(row=3, column=1, padx=10, pady=10)
        
        ttk.Label(dialog, text="密码:", font=self.font).grid(row=4, column=0, sticky=tk.W, padx=20, pady=10)
        password_entry = ttk.Entry(dialog, font=self.font, width=30, show="*")
        password_entry.grid(row=4, column=1, padx=10, pady=10)
        
        # 连接测试按钮
        test_btn = ttk.Button(dialog, text="测试连接", command=lambda:
            self._test_new_connection(name_entry.get().strip(), host_entry.get().strip(),
                                     port_entry.get().strip(), user_entry.get().strip(),
                                     password_entry.get().strip()))
        test_btn.grid(row=5, column=0, columnspan=2, pady=5)
        
        # 创建按钮
        btn_frame = ttk.Frame(dialog)
        btn_frame.grid(row=6, column=0, columnspan=2, pady=5)
        
        def save_connection():
            name = name_entry.get().strip()
            host = host_entry.get().strip()
            port = port_entry.get().strip()
            user = user_entry.get().strip()
            password = password_entry.get().strip()
            
            if not name or not user:
                messagebox.showerror("错误", "连接名称和用户名不能为空")
                return
            
            if app_controller.add_connection(name, host, port, user, password):
                logger.info(f"连接创建成功: {name}")
                messagebox.showinfo("成功", f"连接 '{name}' 创建成功")
                dialog.destroy()
                self.refresh_connections()
            else:
                logger.error(f"连接创建失败: {name}")
                messagebox.showerror("错误", "创建连接失败")
        
        ttk.Button(btn_frame, text="确定", command=save_connection).pack(side=tk.LEFT, padx=20)
        ttk.Button(btn_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)
    
    def _test_new_connection(self, name, host, port, user, password):
        """测试新连接"""
        logger.info(f"测试新连接: 开始执行，名称={name}, 主机={host}, 端口={port}, 用户={user}")
        if not name or not user:
            logger.warning("测试连接失败: 连接名称和用户名不能为空")
            messagebox.showerror("错误", "连接名称和用户名不能为空")
            return
        
        # 在后台线程中直接测试连接，不使用MySQLPasswordManager
        logger.info(f"测试新连接: 准备启动后台线程直接测试连接")
        
        def test_conn():
            logger.info(f"测试新连接: 后台线程开始执行")
            
            # 直接测试连接，不经过存储
            success, message = self._direct_test_connection(host, port, user, password)
            logger.info(f"测试新连接: 连接测试{'成功' if success else '失败'}: {message}")
            
            def show_result():
                logger.info(f"测试新连接: 准备显示测试结果")
                if success:
                    messagebox.showinfo("成功", "连接测试成功")
                else:
                    messagebox.showerror("错误", message)
            
            self.root.after(0, show_result)
        
        thread = threading.Thread(target=test_conn, daemon=True)
        thread.start()
        logger.info(f"测试新连接: 后台线程已启动")
    
    def _direct_test_connection(self, host, port, user, password):
        """直接测试MySQL连接，不经过存储"""
        import pymysql
        try:
            logger.info(f"直接测试连接: 尝试连接到 {host}:{port} 作为用户 {user}")
            # 尝试连接数据库
            conn = pymysql.connect(
                host=host,
                port=int(port) if port else 3306,
                user=user,
                password=password,
                connect_timeout=5
            )
            conn.close()
            logger.info("直接测试连接: 连接成功")
            return True, "连接测试成功"
        except Exception as e:
            logger.error(f"直接测试连接: 连接失败: {str(e)}")
            return False, f"连接失败: {str(e)}"
    
    def edit_connection(self):
        """编辑连接"""
        logger.info("用户点击编辑连接")
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个连接")
            return
        
        item = selected_item[0]
        conn_name = self.tree.item(item, "values")[0] if self.tree.item(item, "values") else None
        
        if conn_name:
            conn_info = self.password_manager.get_password(conn_name)
            if conn_info:
                # 创建编辑对话框（类似于新建对话框）
                dialog = tk.Toplevel(self.root)
                dialog.title("编辑连接")
                dialog.geometry("400x300")
                dialog.resizable(False, False)
                dialog.transient(self.root)
                dialog.grab_set()
                
                # 居中显示
                dialog.update_idletasks()
                width = dialog.winfo_width()
                height = dialog.winfo_height()
                x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
                y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
                dialog.geometry(f'{width}x{height}+{x}+{y}')
                
                # 创建表单
                ttk.Label(dialog, text="连接名称:", font=self.font).grid(row=0, column=0, sticky=tk.W, padx=20, pady=10)
                name_entry = ttk.Entry(dialog, font=self.font, width=30)
                name_entry.insert(0, conn_name)
                name_entry.grid(row=0, column=1, padx=10, pady=10)
                
                ttk.Label(dialog, text="主机名/IP:", font=self.font).grid(row=1, column=0, sticky=tk.W, padx=20, pady=10)
                host_entry = ttk.Entry(dialog, font=self.font, width=30)
                host_entry.insert(0, conn_info['host'])
                host_entry.grid(row=1, column=1, padx=10, pady=10)
                
                ttk.Label(dialog, text="端口:", font=self.font).grid(row=2, column=0, sticky=tk.W, padx=20, pady=10)
                port_entry = ttk.Entry(dialog, font=self.font, width=30)
                port_entry.insert(0, conn_info['port'])
                port_entry.grid(row=2, column=1, padx=10, pady=10)
                
                ttk.Label(dialog, text="用户名:", font=self.font).grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)
                user_entry = ttk.Entry(dialog, font=self.font, width=30)
                user_entry.insert(0, conn_info['username'])
                user_entry.grid(row=3, column=1, padx=10, pady=10)
                
                ttk.Label(dialog, text="密码:", font=self.font).grid(row=4, column=0, sticky=tk.W, padx=20, pady=10)
                password_entry = ttk.Entry(dialog, font=self.font, width=30, show="*")
                password_entry.insert(0, conn_info['password'])
                password_entry.grid(row=4, column=1, padx=10, pady=10)
                
                # 创建按钮
                btn_frame = ttk.Frame(dialog)
                btn_frame.grid(row=5, column=0, columnspan=2, pady=10)
                
                def update_connection():
                    new_name = name_entry.get().strip()
                    host = host_entry.get().strip()
                    port = port_entry.get().strip()
                    user = user_entry.get().strip()
                    password = password_entry.get().strip()
                    
                    if not new_name or not user:
                        messagebox.showerror("错误", "连接名称和用户名不能为空")
                        return
                    
                    # 如果名称改变，先删除旧连接
                    if new_name != conn_name:
                        app_controller.delete_connection(conn_name)
                    
                    if app_controller.add_connection(new_name, host, port, user, password):
                        messagebox.showinfo("成功", f"连接 '{new_name}' 更新成功")
                        dialog.destroy()
                        self.refresh_connections()
                    else:
                        messagebox.showerror("错误", "更新连接失败")
                
                ttk.Button(btn_frame, text="确定", command=update_connection).pack(side=tk.LEFT, padx=20)
                ttk.Button(btn_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)
    
    def delete_connection(self):
        """删除连接"""
        logger.info("用户点击删除连接")
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个连接")
            return
        
        item = selected_item[0]
        conn_name = self.tree.item(item, "values")[0] if self.tree.item(item, "values") else None
        
        if conn_name:
            if messagebox.askyesno("确认删除", f"确定要删除连接 '{conn_name}' 吗？"):
                if app_controller.delete_connection(conn_name):
                    messagebox.showinfo("成功", f"连接 '{conn_name}' 已删除")
                    self.refresh_connections()
                    # 清空详情面板
                    self.name_var.set("")
                    self.host_var.set("")
                    self.port_var.set("")
                    self.user_var.set("")
                    self.password_var.set("")
                else:
                    messagebox.showerror("错误", "删除连接失败")
    
    def copy_password(self):
        """复制密码到剪贴板"""
        logger.info("用户点击复制密码到剪贴板")
        if self.password_var.get():
            self.root.clipboard_clear()
            self.root.clipboard_append(self.password_var.get())
            logger.info(f"密码已复制到剪贴板，连接: {self.selected_connection}")
        else:
            messagebox.showinfo("提示", "请先选择一个连接")
    
    def test_connection(self):
        """测试当前选中的连接"""
        logger.info("用户点击测试连接")
        if self.selected_connection:
            # 在后台线程中测试连接
            threading.Thread(target=self._test_selected_connection, daemon=True).start()
        else:
            messagebox.showinfo("提示", "请先选择一个连接")
    
    def _view_table_info(self):
        """查看表信息"""
        logger.info("用户选择查看表信息")
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个表节点")
            return
        
        item = selected_item[0]
        values = self.tree.item(item, "values")
        
        if not values or values[0] != 'table':
            messagebox.showinfo("提示", "请选择一个表节点")
            return
        
        conn_name = values[1]
        db_name = values[2]
        table_name = values[3]
        
        # 在后台线程中获取表信息
        logger.info(f"开始获取表 {table_name} 的结构信息 (数据库: {db_name}, 连接: {conn_name})")
        
        # 创建等待对话框
        wait_dialog = tk.Toplevel(self.root)
        wait_dialog.title("加载中")
        wait_dialog.geometry("300x100")
        wait_dialog.resizable(False, False)
        wait_dialog.transient(self.root)
        wait_dialog.grab_set()
        
        # 居中显示
        wait_dialog.update_idletasks()
        width = wait_dialog.winfo_width()
        height = wait_dialog.winfo_height()
        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (width // 2)
        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (height // 2)
        wait_dialog.geometry(f"{width}x{height}+{x}+{y}")
        
        # 显示加载信息
        ttk.Label(wait_dialog, text=f"正在获取表 {table_name} 的信息...").pack(pady=20)
        
        # 在后台线程中获取表结构
        def get_table_structure():
            success, result = app_controller.get_table_structure(conn_name, db_name, table_name)
            
            # 在主线程中更新UI
            def update_ui():
                # 关闭等待对话框
                wait_dialog.destroy()
                
                if success:
                    # 创建表信息对话框
                    info_dialog = tk.Toplevel(self.root)
                    info_dialog.title(f"表信息 - {table_name}")
                    info_dialog.geometry("800x600")
                    info_dialog.resizable(True, True)
                    info_dialog.transient(self.root)
                    info_dialog.grab_set()
                    
                    # 创建滚动条
                    scrollbar = ttk.Scrollbar(info_dialog)
                    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
                    
                    # 创建树状视图显示表结构
                    tree = ttk.Treeview(info_dialog, yscrollcommand=scrollbar.set)
                    
                    # 定义列
                    tree['columns'] = ('column_name', 'data_type', 'is_nullable', 'default_value', 'key', 'extra')
                    
                    # 设置列宽
                    tree.column('#0', width=0, stretch=tk.NO)
                    tree.column('column_name', width=150, anchor=tk.W)
                    tree.column('data_type', width=150, anchor=tk.W)
                    tree.column('is_nullable', width=100, anchor=tk.CENTER)
                    tree.column('default_value', width=150, anchor=tk.W)
                    tree.column('key', width=80, anchor=tk.CENTER)
                    tree.column('extra', width=200, anchor=tk.W)
                    
                    # 设置表头
                    tree.heading('column_name', text='列名')
                    tree.heading('data_type', text='数据类型')
                    tree.heading('is_nullable', text='是否可为空')
                    tree.heading('default_value', text='默认值')
                    tree.heading('key', text='键')
                    tree.heading('extra', text='额外信息')
                    
                    # 添加数据
                    for i, col_info in enumerate(result):
                        tree.insert('', tk.END, values=(col_info.get('Field', ''), 
                                                       col_info.get('Type', ''), 
                                                       col_info.get('Null', ''), 
                                                       col_info.get('Default', ''), 
                                                       col_info.get('Key', ''), 
                                                       col_info.get('Extra', '')))
                    
                    # 绑定滚动条
                    scrollbar.config(command=tree.yview)
                    
                    # 显示树状视图
                    tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
                    
                    # 添加关闭按钮
                    btn_frame = ttk.Frame(info_dialog)
                    btn_frame.pack(fill=tk.X, padx=10, pady=10)
                    ttk.Button(btn_frame, text="关闭", command=info_dialog.destroy).pack(side=tk.RIGHT)
                    
                    logger.info(f"成功显示表 {table_name} 的结构信息")
                else:
                    messagebox.showerror("错误", f"获取表信息失败: {result}")
                    logger.error(f"获取表信息失败: {result}")
            
            # 使用after方法在主线程中更新UI
            self.root.after(0, update_ui)
        
        # 启动后台线程
        threading.Thread(target=get_table_structure, daemon=True).start()
        
    def _export_table_structure(self):
        """导出表结构"""
        logger.info("用户选择导出表结构")
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个表节点")
            return
        
        item = selected_item[0]
        values = self.tree.item(item, "values")
        if not values or values[0] != 'table':
            messagebox.showinfo("提示", "请选择一个表节点")
            return
        
        # 获取表信息
        conn_name = values[1]
        db_name = values[2]
        table_name = values[3]
        
        logger.info(f"开始导出表 {table_name} 的结构 (数据库: {db_name}, 连接: {conn_name})")
        
        # 创建等待对话框
        wait_dialog = tk.Toplevel(self.root)
        wait_dialog.title("正在导出")
        wait_dialog.geometry("300x100")
        wait_dialog.resizable(False, False)
        wait_dialog.transient(self.root)
        wait_dialog.grab_set()
        
        # 居中显示
        wait_dialog.update_idletasks()
        width = wait_dialog.winfo_width()
        height = wait_dialog.winfo_height()
        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (width // 2)
        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (height // 2)
        wait_dialog.geometry(f"{width}x{height}+{x}+{y}")
        
        # 显示加载信息
        ttk.Label(wait_dialog, text=f"正在获取表 {table_name} 的建表SQL...").pack(pady=20)
        
        # 在后台线程中获取表结构
        def export_table_structure():
            success, result = app_controller.get_table_create_sql(conn_name, db_name, table_name)
            
            # 在主线程中更新UI
            def update_ui():
                # 关闭等待对话框
                wait_dialog.destroy()
                
                if success:
                    # 让用户选择保存文件的位置
                    default_filename = f"{table_name}_structure.sql"
                    file_path = filedialog.asksaveasfilename(
                        defaultextension=".sql",
                        filetypes=[("SQL文件", "*.sql"), ("所有文件", "*.*")],
                        initialfile=default_filename,
                        title="保存表结构"
                    )
                    
                    if file_path:
                        try:
                            # 写入文件
                            with open(file_path, 'w', encoding='utf-8') as f:
                                f.write(result)
                            messagebox.showinfo("成功", f"表结构已成功导出到:\n{file_path}")
                            logger.info(f"表结构已成功导出到: {file_path}")
                        except Exception as e:
                            messagebox.showerror("错误", f"保存文件失败:\n{str(e)}")
                            logger.error(f"保存文件失败: {str(e)}")
                else:
                    messagebox.showerror("错误", f"获取表结构失败: {result}")
                    logger.error(f"获取表结构失败: {result}")
            
            # 使用after方法在主线程中更新UI
            self.root.after(0, update_ui)
        
        # 启动后台线程
        threading.Thread(target=export_table_structure, daemon=True).start()
        
    def _load_tables_for_selected_database(self):
        """为选中的数据库节点加载表列表"""
        logger.info("用户通过右键菜单选择加载表")
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个数据库节点")
            return
        
        item = selected_item[0]
        values = self.tree.item(item, "values")
        
        if not values or values[0] != 'database':
            messagebox.showinfo("提示", "请选择一个数据库节点")
            return
        
        conn_name = values[1]
        db_name = values[2]
        
        # 清空数据库节点的子节点
        for child in self.tree.get_children(item):
            self.tree.delete(child)
        
        # 添加一个临时的"加载中"节点
        self.tree.insert(item, 'end', text='加载表中...', values=('loading',))
        
        # 在后台线程中加载表列表
        logger.info(f"开始加载数据库 {db_name} 的表列表")
        threading.Thread(target=self._load_tables_for_database, 
                        args=(conn_name, db_name, item), 
                        daemon=True).start()
    
    def _test_selected_connection(self):
        """测试选中的连接"""
        logger.info(f"测试连接: {self.selected_connection}")
        success, message = app_controller.test_connection(self.selected_connection)
        
        def show_result():
            if success:
                messagebox.showinfo("成功", "连接测试成功")
                self.status_var.set("已连接")
            else:
                messagebox.showerror("错误", message)
                self.status_var.set(f"连接失败: {message}")
        
        self.root.after(0, show_result)
    
    def on_database_select(self):
        """数据库选择事件处理"""
        if self.db_combobox.get():
            self.selected_database = self.db_combobox.get()
            logger.info(f"用户选择数据库: {self.selected_database} (连接: {self.selected_connection})")
    
    def execute_query(self):
        """执行SQL查询"""
        logger.info("用户点击执行SQL查询")
        if not self.selected_connection or not self.selected_database:
            messagebox.showinfo("提示", "请先选择连接和数据库")
            return
        
        query = self.query_text.get("1.0", tk.END).strip()
        if not query:
            messagebox.showinfo("提示", "请输入SQL查询语句")
            return
        
        # 分割SQL语句（支持分号分隔的多条语句）
        queries = [q.strip() for q in query.split(';') if q.strip()]
        
        if not queries:
            messagebox.showinfo("提示", "请输入有效的SQL查询语句")
            return
        
        # 在后台线程中执行查询
        threading.Thread(target=self._execute_query_async, args=(queries,), daemon=True).start()
    
    def _execute_query_async(self, queries):
        """异步执行查询"""
        logger.info(f"开始执行查询，共 {len(queries)} 条语句")
        
        # 显示加载状态
        def show_loading():
            self.is_loading = True
            self.status_var.set("正在执行查询...")
            # 显示进度条并开始动画
            self.progress_frame.pack(fill=tk.X, padx=10, pady=5)
            self.progress_bar.start()
        
        self.root.after(0, show_loading)
        
        # 存储所有查询结果
        all_results = []
        
        # 逐条执行查询
        for i, query in enumerate(queries):
            logger.info(f"执行查询 {i+1}/{len(queries)}: {query[:100]}...")
            try:
                success, results, columns = app_controller.execute_query(
                    self.selected_connection, self.selected_database, query
                )
                logger.info(f"查询 {i+1} 执行完成，成功: {success}")
                all_results.append((query, success, results, columns))
            except Exception as e:
                logger.exception(f"查询 {i+1} 执行异常")
                all_results.append((query, False, str(e), None))
        
        # 更新UI显示结果
        def show_results():
            self.is_loading = False
            # 隐藏进度条
            self.progress_bar.stop()
            self.progress_frame.pack_forget()
            
            # 获取当前查询页签的索引
            current_query_idx = self.current_query_index
            
            # 确保query_result_map的长度足够
            while len(self.query_result_map) <= current_query_idx:
                self.query_result_map.append([])
            
            # 初始化result_tabs变量
            result_tabs = []
            
            # 获取当前查询页签关联的结果页签 - 这里有条件地执行，以避免AttributeError
            try:
                if hasattr(self, 'results_notebook') and current_query_idx >= 0 and self.query_result_map[current_query_idx]:
                    # 有已关联的结果页签，使用第一个
                    result_tab_index = self.query_result_map[current_query_idx][0]
                    
                    # 确保索引有效
                    if 0 <= result_tab_index < len(self.results_notebook.tabs()):
                        # 清理所有与当前查询相关的旧结果页签
                        for idx in reversed(self.query_result_map[current_query_idx]):
                            if 0 <= idx < len(self.results_notebook.tabs()):
                                self.results_notebook.forget(idx)
                        
                        # 清空映射关系
                        self.query_result_map[current_query_idx] = []
                        
                        # 为每条查询结果创建一个新的结果标签页
                        for i, (query, success, results, columns) in enumerate(all_results):
                            tab_title = f"结果 {i+1}"
                            result_tab, result_tree = self._create_new_result_tab(tab_title)
                            
                            # 添加到结果笔记本
                            self.results_notebook.add(result_tab, text=tab_title)
                            result_tab_index = len(self.results_notebook.tabs()) - 1
                            
                            # 更新映射关系
                            self.query_result_map[current_query_idx].append(result_tab_index)
                            
                            result_tabs.append((query, success, results, columns, result_tree))
            except Exception as e:
                logger.warning(f"处理结果页签关联时出错: {str(e)}")
            
            # 组合标签页结构中，实现多子页签展示
            current_tab = self.combined_notebook.select()
            if current_tab and not result_tabs:
                # 查找当前组合标签页对应的结果树和结果框架
                for idx, (_, old_result_tree, tab, _, result_frame) in enumerate(self.query_result_pairs):
                    if str(tab) == str(current_tab):
                        # 检查结果框架中是否已经有结果子页签笔记本
                        result_sub_notebook = None
                        for child in result_frame.winfo_children():
                            if isinstance(child, ttk.Notebook):
                                result_sub_notebook = child
                                break
                        
                        if not result_sub_notebook:
                            # 如果没有，创建一个新的笔记本用于显示多个结果子页签
                            # 先清除原有的结果树
                            for child in result_frame.winfo_children():
                                child.pack_forget()
                                child.destroy()
                            
                            # 创建结果子页签笔记本
                            result_sub_notebook = ttk.Notebook(result_frame)
                            result_sub_notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
                            
                            # 记录笔记本对象，方便后续访问
                            self.result_sub_notebooks = getattr(self, 'result_sub_notebooks', {})
                            self.result_sub_notebooks[str(tab)] = result_sub_notebook
                        else:
                            # 如果有，清空所有现有的子页签
                            for sub_tab in result_sub_notebook.tabs():
                                result_sub_notebook.forget(sub_tab)
                        
                        # 为每条查询结果创建一个新的结果子页签
                        for i, (query, success, results, columns) in enumerate(all_results):
                            # 创建子页签
                            sub_tab = ttk.Frame(result_sub_notebook)
                            sub_tab_title = f"结果 {i+1}"
                            result_sub_notebook.add(sub_tab, text=sub_tab_title)
                            
                            # 创建结果树
                            result_tree = ttk.Treeview(sub_tab, show="headings", selectmode="extended")
                            result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
                            
                            # 添加结果树滚动条
                            result_y_scrollbar = ttk.Scrollbar(sub_tab, orient=tk.VERTICAL, command=result_tree.yview)
                            result_y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
                            
                            result_x_scrollbar = ttk.Scrollbar(sub_tab, orient=tk.HORIZONTAL, command=result_tree.xview)
                            result_x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
                            
                            result_tree.config(yscrollcommand=result_y_scrollbar.set, xscrollcommand=result_x_scrollbar.set)
                            
                            # 设置选中时可复制
                            result_tree.bind("<Button-1>", lambda event, tree=result_tree: self._on_result_tree_click(event, tree))
                            
                            result_tabs.append((query, success, results, columns, result_tree))
                        
                        # 如果只有一个结果，保持简单显示
                        if len(all_results) == 1:
                            result_sub_notebook.pack_forget()
                            result_tree = result_tabs[0][4]
                            result_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
                            result_y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
                            result_x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
                            
                        break
            
            logger.info(f"准备显示 {len(result_tabs)} 个查询结果")
            
            # 显示每条查询的结果
            success_count = 0
            for i, (query, success, results, columns, result_tree) in enumerate(result_tabs):
                logger.info(f"处理查询结果 {i+1}/{len(result_tabs)}, 成功: {success}")
                
                # 确保result_tree不为None
                if not result_tree:
                    logger.error(f"查询结果 {i+1} 的result_tree为None")
                    continue
                
                try:
                    if success:
                        success_count += 1
                        
                        # 记录结果和列信息
                        logger.info(f"查询 {i+1} 结果: {len(results) if results else 0} 行, 列数: {len(columns) if columns else 0}")
                        
                        # 设置列
                        if columns:
                            # 添加序号列到列列表的最前面
                            columns_with_index = ['序号'] + columns
                            result_tree['columns'] = columns_with_index
                            
                            # 配置序号列
                            result_tree.heading('序号', text='序号')
                            result_tree.column('序号', width=50, minwidth=50, anchor=tk.CENTER, stretch=False)
                            
                            # 配置其他列 - 添加最小宽度和禁用自动拉伸，防止拖动后挤在一起
                            for col in columns:
                                result_tree.heading(col, text=col)
                                # 设置合适的宽度和最小宽度，禁用自动拉伸
                                result_tree.column(col, width=150, minwidth=80, anchor=tk.CENTER, stretch=False)
                            
                            # 添加数据，限制最多显示行数
                            try:
                                max_rows = int(self.max_rows_var.get())
                            except ValueError:
                                max_rows = 1000  # 默认值
                            
                            if results:
                                # 限制显示的行数不超过max_rows
                                display_results = results[:max_rows]
                                
                                for idx, row in enumerate(display_results, 1):
                                    try:
                                        values = [str(idx)] + ['' if row[col] is None else str(row[col]) for col in columns]
                                        result_tree.insert('', tk.END, values=values)
                                    except Exception as e:
                                        logger.error(f"添加数据行异常")
                                
                                # 如果结果行数超过了最大限制，显示提示信息
                                if len(results) > max_rows:
                                    logger.info(f"查询结果已限制显示前{max_rows}行，共{len(results)}行")
                        else:
                            # 对于非查询语句，显示受影响的行数
                            if results and isinstance(results, list) and results and "affected_rows" in results[0]:
                                # 创建一个特殊的结果树来显示影响行数
                                result_tree['columns'] = ['result']
                                result_tree.heading('result', text='结果')
                                result_tree.column('result', width=300, anchor=tk.CENTER)
                                result_tree.insert('', tk.END, values=[f"查询执行成功，影响行数: {results[0]['affected_rows']}"])
                            else:
                                # 如果没有受影响行数信息，显示简单的成功消息
                                result_tree['columns'] = ['result']
                                result_tree.heading('result', text='结果')
                                result_tree.column('result', width=300, anchor=tk.CENTER)
                                result_tree.insert('', tk.END, values=["查询执行成功"])
                    else:
                        # 创建一个特殊的结果树来显示错误信息
                        result_tree['columns'] = ['error']
                        result_tree.heading('error', text='错误信息')
                        result_tree.column('error', width=700, anchor=tk.W, stretch=True)
                        error_msg = str(results) if results else "未知错误"
                        
                        # 自动换行显示错误信息 - 使用简单的方法处理
                        full_error_msg = f"查询执行失败: {error_msg}"
                        # 在Tkinter的Treeview中，\n通常可以实现换行，但需要确保列足够宽
                        result_tree.insert('', tk.END, values=[full_error_msg])
                except Exception as e:
                        logger.error(f"显示查询结果 {i+1} 时发生异常")
                        # 显示错误信息到结果树
                        result_tree['columns'] = ['error']
                        result_tree.heading('error', text='显示异常')
                        result_tree.column('error', width=700, anchor=tk.W, stretch=True)
                        exception_msg = f"显示结果时发生异常: {str(e)}"
                        result_tree.insert('', tk.END, values=[exception_msg])
            
            # 更新状态
            logger.info(f"查询执行完成，总查询数: {len(all_results)}, 成功数: {success_count}")
            if success_count == len(all_results):
                self.status_var.set(f"所有 {len(all_results)} 条查询执行完成")
            elif success_count > 0:
                self.status_var.set(f"{success_count}/{len(all_results)} 条查询执行成功")
            else:
                self.status_var.set("所有查询执行失败")
            
            # 如果有结果标签页，选择当前查询页签对应的结果页签
            if self.results_notebook.tabs():
                # 检查当前查询页签是否有对应的结果页签
                if current_query_idx >= 0 and self.query_result_map[current_query_idx]:
                    # 获取当前查询页签对应的第一个结果页签的索引
                    result_tab_index = self.query_result_map[current_query_idx][0]
                    # 确保索引有效
                    if 0 <= result_tab_index < len(self.results_notebook.tabs()):
                        # 选择对应的结果页签
                        self.results_notebook.select(result_tab_index)
        
        self.root.after(0, show_results)
    
    def clear_query(self):
        """清空查询编辑器"""
        logger.info("用户点击清空查询编辑器")
        if self.current_query_index >= 0 and self.current_query_index < len(self.query_editors):
            self.query_editors[self.current_query_index].delete("1.0", tk.END)
    
    def _create_new_query_editor(self):
        """创建一个新的查询编辑器，以页签方式展示"""
        self.query_tab_count += 1
        
        # 创建标签页内容
        query_tab = ttk.Frame(self.query_editor_notebook)
        
        # 创建查询文本编辑器
        query_text = scrolledtext.ScrolledText(query_tab, wrap=tk.WORD, font=('Consolas', 10), height=10)
        query_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加到编辑器列表
        self.query_editors.append(query_text)
        self.query_editor_frames.append(query_tab)
        self.current_query_index = len(self.query_editors) - 1
        
        # 更新query_text引用以保持向后兼容性
        self.query_text = query_text
        
        # 添加标签页到笔记本
        tab_text = f"查询 {self.query_tab_count} ×"
        self.query_editor_notebook.add(query_tab, text=tab_text)
        
        # 选择新创建的标签页
        self.query_editor_notebook.select(query_tab)
        
        # 确保新的查询编辑器获取焦点
        query_text.focus_set()
        
        # 添加右键菜单
        self._create_query_editor_context_menu(query_text)
        
        # 绑定标签页切换事件，更新当前查询编辑器引用
        self.query_editor_notebook.bind("<<NotebookTabChanged>>", self._on_query_tab_changed)
        
        # 绑定标签页点击事件，实现关闭功能
        query_tab.bind("<Button-1>", lambda event, idx=self.current_query_index: self._on_tab_click(event, idx))
        
    def _on_query_tab_changed(self, event=None):
        """处理查询标签页切换事件"""
        # 获取当前选中的标签页
        current_tab = self.query_editor_notebook.select()
        
        # 查找对应的索引
        for i, frame in enumerate(self.query_editor_frames):
            if str(frame) == current_tab:
                self.current_query_index = i
                self.query_text = self.query_editors[i]
                
                # 如果有对应的结果页签，切换到第一个结果页签
                if i < len(self.query_result_map) and self.query_result_map[i]:
                    # 获取结果页签的索引
                    result_tab_index = self.query_result_map[i][0]
                    
                    # 确保结果页签索引有效
                    tabs = self.results_notebook.tabs()
                    if tabs and 0 <= result_tab_index < len(tabs):
                        self.results_notebook.select(tabs[result_tab_index])
                break
                
    def _on_result_tab_changed(self, event=None):
        """处理结果标签页切换事件，实现反向联动到查询页签"""
        # 获取当前选中的结果标签页
        current_tab = self.results_notebook.select()
        if not current_tab:
            return
        
        # 获取当前结果标签页的索引
        try:
            current_result_index = self.results_notebook.index(current_tab)
            logger.info(f"结果标签页切换: 当前结果页签索引 {current_result_index}")
        except TclError:
            logger.error("获取结果标签页索引失败")
            return
        
        # 查找对应的查询页签
        found = False
        for query_idx, result_indices in enumerate(self.query_result_map):
            if result_indices and current_result_index in result_indices:
                # 找到了对应的查询页签
                logger.info(f"找到对应的查询页签: 结果页签索引 {current_result_index} -> 查询页签索引 {query_idx}")
                if 0 <= query_idx < len(self.query_editor_frames):
                    # 切换到对应的查询页签
                    self.query_editor_notebook.select(self.query_editor_frames[query_idx])
                    # 设置当前查询索引
                    self.current_query_index = query_idx
                    self.query_text = self.query_editors[query_idx]
                    logger.info(f"已切换到对应查询页签: 索引 {query_idx}")
                    found = True
                break
        
        if not found:
            logger.info(f"未找到与结果页签 {current_result_index} 对应的查询页签")                
    def _on_combined_tab_changed(self, event=None):
        """处理组合标签页切换事件"""
        # 获取当前选中的组合标签页索引
        current_tab = self.combined_notebook.select()
        if not current_tab:
            return
        
        # 获取当前组合标签页的索引
        try:
            current_index = self.combined_notebook.index(current_tab)
            logger.info(f"组合标签页切换: 当前索引 {current_index}")
            
            # 设置当前查询索引
            self.current_query_index = current_index
            
            # 更新当前查询文本引用
            if 0 <= current_index < len(self.query_result_pairs):
                self.query_text = self.query_result_pairs[current_index][0]  # 查询编辑器
        except Exception as e:
            logger.error(f"处理组合标签页切换时出错: {str(e)}")
            
    def _update_query_tab_title(self, combined_tab):
        """更新查询标签页的标题，使其显示查询内容的前几个字符"""
        # 查找对应的查询编辑器
        for editor, _, tab, _, _ in self.query_result_pairs:
            if str(tab) == str(combined_tab):
                # 获取查询文本的前几个字符
                query_text = editor.get("1.0", "1.20").strip()
                if query_text:
                    # 查找对应的标签页索引
                    for i, tab_id in enumerate(self.combined_notebook.tabs()):
                        if str(tab_id) == str(combined_tab):
                            # 更新标签页标题
                            self.combined_notebook.tab(i, text=f"{query_text[:15]}...")
                            break
                break
                
    def _add_close_button_to_tab(self, tab, notebook):
        """为标签页添加关闭按钮"""
        # 注意：TTK Notebook不直接支持为标签添加按钮
        # 这里我们采用一种常见的技巧，将关闭符号添加到标签文本中
        # 并通过点击事件来处理关闭操作
        
        # 获取标签页索引
        for i, tab_id in enumerate(notebook.tabs()):
            if str(tab_id) == str(tab):
                # 查找对应的查询编辑器索引
                for idx, (_, _, pair_tab, _, _) in enumerate(self.query_result_pairs):
                    if str(pair_tab) == str(tab):
                        # 修改标签页文本，添加关闭符号
                        current_text = notebook.tab(i, "text")
                        if "×" not in current_text:
                            notebook.tab(i, text=f"{current_text} ×")
                        break
                break
                
    def _close_combined_tab(self, index):
        """关闭指定索引的组合标签页"""
        if 0 <= index < len(self.query_result_pairs):
            # 获取要关闭的标签页
            _, _, tab_to_close, _, _ = self.query_result_pairs[index]
            
            # 从notebook中移除标签页
            self.combined_notebook.forget(tab_to_close)
            
            # 从列表中移除
            del self.query_result_pairs[index]
            
            # 如果关闭的是当前标签页，更新当前索引
            if index <= self.current_query_index:
                # 如果还有其他标签页，更新当前索引
                if self.query_result_pairs:
                    self.current_query_index = min(index, len(self.query_result_pairs) - 1)
                    self.query_text = self.query_result_pairs[self.current_query_index][0]
                else:
                    # 如果没有标签页了，创建一个新的
                    self.current_query_index = -1
                    self.query_text = None
                    self.new_query()
                    
    def _on_combined_tab_click(self, event):
        """处理组合标签页点击事件，实现关闭功能"""
        # 获取当前选中的标签页
        current_tab = self.combined_notebook.select()
        if not current_tab:
            return
            
        # 获取当前标签页的索引
        try:
            current_index = self.combined_notebook.index(current_tab)
            # 获取标签页文本
            tab_text = self.combined_notebook.tab(current_index, "text")
            
            # 检查文本是否包含关闭标记
            if "×" in tab_text:
                # 关闭当前标签页
                self._close_combined_tab(current_index)
        except Exception as e:
            logger.error(f"处理组合标签页点击事件时出错: {str(e)}")
            
    def _on_tab_click(self, event, index):
        """处理标签页点击事件，实现关闭功能"""
        # 这是旧的查询编辑器标签页的处理方法，现在我们主要使用组合标签页
        # 保留这个方法以确保向后兼容性
        try:
            # 获取标签页文本
            tab_idx = self.query_editor_notebook.index(self.query_editor_frames[index])
            tab_text = self.query_editor_notebook.tab(tab_idx, "text")
            
            # 检查点击位置是否在关闭按钮附近
            if self.query_editor_notebook.select() == str(self.query_editor_frames[index]) and "×" in tab_text:
                # 模拟点击关闭按钮的行为
                self._close_query_editor(index)
        except:
            pass
    
    def _close_query_editor(self, index):
        """关闭指定索引的查询编辑器"""
        if 0 <= index < len(self.query_editors):
            # 获取要关闭的标签页
            tab_to_close = self.query_editor_frames[index]
            
            # 从notebook中移除标签页
            self.query_editor_notebook.forget(tab_to_close)
            
            # 从列表中移除
            del self.query_editors[index]
            del self.query_editor_frames[index]
            
            # 清理对应的查询结果页签映射关系
            if index < len(self.query_result_map):
                del self.query_result_map[index]
            
            # 更新剩余的映射关系，调整索引
            for i in range(index, len(self.query_result_map)):
                for j in range(len(self.query_result_map[i])):
                    # 索引需要调整吗？可能不需要，因为结果页签的物理索引不会自动变化
                    pass
            
            # 如果关闭的是当前编辑器，更新当前索引
            if index <= self.current_query_index:
                # 如果还有其他标签页，更新当前索引
                if self.query_editors:
                    self.current_query_index = min(index, len(self.query_editors) - 1)
                    self.query_text = self.query_editors[self.current_query_index]
                else:
                    # 如果没有标签页了，创建一个新的
                    self.current_query_index = -1
                    self.query_text = None
                    self._create_new_query_editor()
            
    def _rearrange_query_editors(self):
        """重新排列所有查询编辑器"""
        # 先移除所有框架
        for frame in self.query_editor_frames:
            frame.pack_forget()
            
            # 移除可能存在的关闭按钮
            for child in frame.winfo_children():
                if isinstance(child, ttk.Button) and child['text'] == '×':
                    child.destroy()
        
        # 如果没有编辑器了，创建一个新的
        if not self.query_editors:
            self._create_new_query_editor()
            return
        
        # 重新布局所有编辑器，水平排列
        for i, frame in enumerate(self.query_editor_frames):
            # 设置权重，使所有编辑器平分空间
            frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
            
            # 添加关闭按钮到每个编辑器框架
            close_btn = ttk.Button(frame, text="×", width=2, command=lambda idx=i: self._close_query_editor(idx))
            close_btn.place(relx=1.0, rely=0.0, anchor="ne", x=-5, y=5)
        
        # 更新当前查询文本引用
        self.query_text = self.query_editors[self.current_query_index]
        
    def get_current_query_text(self):
        """获取当前查询编辑器的文本"""
        if self.current_query_index >= 0 and self.current_query_index < len(self.query_editors):
            return self.query_editors[self.current_query_index].get("1.0", tk.END).strip()
        return ""
        
    def new_query(self):
        """新建查询，同时创建查询区和结果区的组合标签页"""
        logger.info("用户点击新建查询")
        # 切换到查询标签页
        self.notebook.select(self.query_tab)
        
        # 增加查询和结果计数器
        self.query_tab_count += 1
        self.result_tab_count += 1
        
        # 创建组合标签页
        combined_tab = ttk.Frame(self.combined_notebook)
        self.combined_notebook.add(combined_tab, text=f"查询 {self.query_tab_count}")
        
        # 创建垂直分割窗格，放置查询编辑器和结果树
        paned_window = ttk.PanedWindow(combined_tab, orient=tk.VERTICAL)
        paned_window.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 上部区域 - 查询编辑器
        query_frame = ttk.LabelFrame(paned_window, text="SQL查询")
        paned_window.add(query_frame, weight=2)  # 查询区占2/3高度
        
        # 创建查询编辑器
        query_editor = tk.Text(query_frame, wrap=tk.NONE, font=self.font, undo=True)
        # 配置标签用于语法高亮
        query_editor.tag_configure("keyword", foreground="blue")
        # 配置标签用于字符串高亮
        query_editor.tag_configure("string", foreground="#16a951")
        query_editor.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加滚动条
        y_scrollbar = ttk.Scrollbar(query_frame, orient=tk.VERTICAL, command=query_editor.yview)
        y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        x_scrollbar = ttk.Scrollbar(query_frame, orient=tk.HORIZONTAL, command=query_editor.xview)
        x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        
        query_editor.config(yscrollcommand=y_scrollbar.set, xscrollcommand=x_scrollbar.set)
        
        # 为查询编辑器绑定事件
        query_editor.bind("<KeyRelease>", lambda event, q=query_editor: self._on_query_text_change(event, q, combined_tab))
        query_editor.bind("<Control-Key-r>", lambda event: self.execute_query())
        query_editor.bind("<Control-Key-R>", lambda event: self.execute_query())
        
        # 添加右键菜单
        self._create_query_editor_context_menu(query_editor)
        
        # 下部区域 - 查询结果
        result_frame = ttk.LabelFrame(paned_window, text="查询结果")
        paned_window.add(result_frame, weight=1)  # 结果区占1/3高度
        
        # 创建结果树
        result_tree = ttk.Treeview(result_frame, show="headings", selectmode="extended")
        result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加结果树滚动条
        result_y_scrollbar = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=result_tree.yview)
        result_y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        result_x_scrollbar = ttk.Scrollbar(result_frame, orient=tk.HORIZONTAL, command=result_tree.xview)
        result_x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        
        result_tree.config(yscrollcommand=result_y_scrollbar.set, xscrollcommand=result_x_scrollbar.set)
        
        # 设置选中时可复制
        result_tree.bind("<Button-1>", lambda event: self._on_result_tree_click(event, result_tree))
        
        # 为当前选中的标签页添加关闭按钮
        self._add_close_button_to_tab(combined_tab, self.combined_notebook)
        
        # 记录查询编辑器和结果树的对应关系
        self.query_result_pairs.append((query_editor, result_tree, combined_tab, query_frame, result_frame))
        
        # 切换到新创建的标签页
        self.combined_notebook.select(combined_tab)
        
        # 确保分隔线位置合理（在第一次显示后调整）
        def set_initial_sash_position():
            try:
                paned_window.update_idletasks()
                height = paned_window.winfo_height()
                if height > 100:  # 确保有足够高度
                    paned_window.sashpos(0, int(height * 0.66))  # 查询区占2/3高度
            except Exception:
                pass
        
        # 在标签页显示后调整分隔线位置
        combined_tab.after(100, set_initial_sash_position)
    
    def export_results(self):
        """导出查询结果"""
        logger.info("用户点击导出查询结果")
        
        # 查找结果树 - 首先尝试从组合标签页结构中查找
        result_tree = None
        
        # 检查是否是组合标签页结构
        current_tab = self.combined_notebook.select()
        if current_tab:
            # 查找当前组合标签页对应的结果树
            for idx, (_, _, tab, _, result_frame) in enumerate(self.query_result_pairs):
                if str(tab) == str(current_tab):
                    # 检查结果框架中是否有结果子页签笔记本
                    result_sub_notebook = None
                    for child in result_frame.winfo_children():
                        if isinstance(child, ttk.Notebook):
                            result_sub_notebook = child
                            break
                    
                    if result_sub_notebook:
                        # 有子页签笔记本，获取当前选中的子页签
                        current_sub_tab = result_sub_notebook.select()
                        if current_sub_tab:
                            # 在当前子页签中查找结果树
                            for child in result_sub_notebook.winfo_children():
                                if str(child) == current_sub_tab:
                                    for tree_child in child.winfo_children():
                                        if isinstance(tree_child, ttk.Treeview):
                                            result_tree = tree_child
                                            break
                                    break
                    else:
                        # 没有子页签笔记本，直接在结果框架中查找结果树
                        for child in result_frame.winfo_children():
                            if isinstance(child, ttk.Treeview):
                                result_tree = child
                                break
                    
                    break
        
        # 如果在组合标签页结构中没有找到结果树，尝试从传统的结果笔记本中查找
        if not result_tree:
            # 检查是否有结果标签页
            if not hasattr(self, 'results_notebook') or not self.results_notebook.tabs():
                messagebox.showinfo("提示", "没有可导出的查询结果")
                return
            
            # 获取当前选中的标签页
            current_tab = self.results_notebook.select()
            
            # 查找对应的结果树
            for child in self.results_notebook.winfo_children():
                if str(child) == current_tab:
                    for subchild in child.winfo_children():
                        if isinstance(subchild, ttk.Frame):
                            for tree_child in subchild.winfo_children():
                                if isinstance(tree_child, ttk.Treeview):
                                    result_tree = tree_child
                                    break
                            break
                    break
        
        if not result_tree:
            messagebox.showinfo("提示", "未能找到结果树对象")
            return
        
        # 获取列名
        columns = result_tree['columns']
        if not columns or len(columns) <= 1:
            messagebox.showinfo("提示", "没有可导出的数据")
            return
        
        # 跳过序号列
        data_columns = columns[1:] if columns[0] == '序号' else columns
        
        # 获取数据
        data = []
        for item in result_tree.get_children():
            values = result_tree.item(item, 'values')
            if values:
                # 跳过序号列
                data_row = values[1:] if len(values) > 0 and columns[0] == '序号' else values
                data.append(data_row)
        
        if not data:
            messagebox.showinfo("提示", "没有可导出的数据")
            return
        
        # 弹出保存文件对话框
        file_types = [
            ("SQL INSERT 脚本", "*.sql"),
            ("Excel 97-2003", "*.xls"),
            ("Excel Workbook", "*.xlsx"),
            ("CSV 文件", "*.csv"),
            ("所有文件", "*.*")
        ]
        
        file_path = filedialog.asksaveasfilename(
            title="导出查询结果",
            defaultextension=".sql",
            filetypes=file_types
        )
        
        if not file_path:
            return  # 用户取消了保存
        
        # 根据文件扩展名选择导出格式
        try:
            import pandas as pd
            import os
            
            # 创建DataFrame
            df = pd.DataFrame(data, columns=data_columns)
            
            # 获取文件扩展名并转换为小写
            _, ext = os.path.splitext(file_path)
            ext = ext.lower()
            
            if ext == '.sql':
                # 导出为INSERT脚本
                table_name = "exported_data"
                with open(file_path, 'w', encoding='utf-8') as f:
                    for _, row in df.iterrows():
                        # 处理字段值，添加引号等
                        values = []
                        for col, val in zip(data_columns, row):
                            if pd.isna(val):
                                values.append('NULL')
                            else:
                                # 转义引号并添加引号
                                val_str = str(val).replace("'", "''")
                                values.append(f"'{val_str}'")
                        
                        # 构建INSERT语句
                        insert_sql = f"INSERT INTO {table_name} ({', '.join(data_columns)}) VALUES ({', '.join(values)});\n"
                        f.write(insert_sql)
                
                messagebox.showinfo("成功", f"查询结果已成功导出到 {file_path}")
                
            elif ext == '.xls':
                # 导出为Excel 97-2003格式
                df.to_excel(file_path, index=False, engine='xlwt')
                messagebox.showinfo("成功", f"查询结果已成功导出到 {file_path}")
                
            elif ext == '.xlsx':
                # 导出为Excel Workbook格式
                df.to_excel(file_path, index=False, engine='openpyxl')
                messagebox.showinfo("成功", f"查询结果已成功导出到 {file_path}")
                
            elif ext == '.csv':
                # 导出为CSV格式
                df.to_csv(file_path, index=False, encoding='utf-8-sig')
                messagebox.showinfo("成功", f"查询结果已成功导出到 {file_path}")
                
            else:
                messagebox.showerror("错误", f"不支持的文件格式: {ext}")
                
        except ImportError as e:
            logger.error(f"导入模块失败: {e}")
            messagebox.showerror("错误", f"导入必要模块失败: {e}\n请尝试重新安装依赖包")
        except Exception as e:
            logger.error(f"导出结果失败: {e}")
            messagebox.showerror("错误", f"导出结果失败: {e}")
    
    def show_about(self):
        """显示关于对话框"""
        logger.info("用户点击显示关于对话框")
        messagebox.showinfo("关于", "mysqlpass - 自由使用的MySql工具!!  Freedam\n版本: v1.0.0\n\n一个简单实用的MySQL工具")
        
    def check_for_updates(self):
        """检查更新"""
        logger.info("用户点击检查更新")
        
        # 使用控制器执行检查更新
        app_controller.check_for_updates(callback=self._on_update_check_complete)
        
        # 显示检查更新的状态
        self.status_var.set("正在检查更新...")
        
    def show_suggestion_dialog(self):
        """显示优化建议对话框"""
        logger.info("用户点击优化建议")
        
        # 创建对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("优化建议")
        # 降低对话框初始高度，从350改为280
        dialog.geometry("500x280")
        dialog.resizable(True, True)
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
        y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
        dialog.geometry(f'{width}x{height}+{x}+{y}')
        
        # 创建标签
        ttk.Label(dialog, text="请输入您对软件的优化建议：", font=self.font).pack(padx=15, pady=10, anchor=tk.W)
        
        # 创建文本框
        text_frame = ttk.Frame(dialog)
        # 调整text_frame的pack参数，使其高度减小
        text_frame.pack(fill=tk.BOTH, expand=False, padx=15, pady=5)
        
        # 创建文本框并设置高度参数，将高度降低三分之一
        suggestion_text = tk.Text(text_frame, font=('Consolas', 10), wrap=tk.WORD, height=8)  # 设置固定行数
        suggestion_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=False)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=suggestion_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        suggestion_text.config(yscrollcommand=scrollbar.set)
        
        # 创建按钮框架
        btn_frame = ttk.Frame(dialog)
        btn_frame.pack(fill=tk.X, padx=15, pady=10)

        # 提交建议按钮
        def submit_suggestion():
            suggestion = suggestion_text.get("1.0", tk.END).strip()
            if suggestion:
                logger.info("用户提交优化建议")
                # 这里可以添加保存建议的逻辑
                # 目前只是显示成功提示
                messagebox.showinfo("提交成功", "感谢您的宝贵建议！我们会认真考虑您的意见。")
                dialog.destroy()
            else:
                messagebox.showwarning("提示", "请输入您的建议内容")
                
        # 创建更醒目的提交建议按钮
        submit_button = ttk.Button(btn_frame, text="提交建议", command=submit_suggestion, width=15)
        submit_button.pack(side=tk.RIGHT, padx=10, pady=5)
        cancel_button = ttk.Button(btn_frame, text="取消", command=dialog.destroy, width=10)
        cancel_button.pack(side=tk.RIGHT, padx=5, pady=5)
        
        # 确保对话框初始尺寸足够大，能够完整显示所有按钮
        dialog.update_idletasks()
        min_width = max(dialog.winfo_width(), 500)
        # 调整最小高度，从350改为280
        min_height = max(dialog.winfo_height(), 280)
        dialog.geometry(f"{min_width}x{min_height}+{x}+{y}")
    
    def show_donation_dialog(self):
        """显示自愿打赏对话框"""
        logger.info("用户点击自愿打赏")
        
        # 创建对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("自愿打赏")
        dialog.geometry("600x500")
        dialog.resizable(True, True)
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.update_idletasks()
        width = dialog.winfo_width()
        height = dialog.winfo_height()
        x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
        y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
        dialog.geometry(f'{width}x{height}+{x}+{y}')
        
        # 创建滚动区域
        canvas = tk.Canvas(dialog)
        scrollbar = ttk.Scrollbar(dialog, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)
        
        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )
        
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 放置画布和滚动条
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        # 创建文本标签
        donation_text = "本软件不需要验证码，不需要授权码，不强制收费，服务器和软件都是我利用闲暇时间开发的，有很多时候想要放弃，但是一看到navicat的授权码，我就想搞死它；所以决心做一款完全免费的mysql工具；打赏全凭自愿，赏钱随意，您的一点点赞赏，是我前行的动力。"
        
        ttk.Label(scrollable_frame, text=donation_text, font=self.font, wraplength=550, justify=tk.LEFT).pack(padx=20, pady=15)
        
        # 尝试加载图片
        try:
            # 从URL加载图片
            import urllib.request
            from io import BytesIO
            
            # 指定图片URL
            image_url = "http://localhost:8080/static/wechat.png"
            
            # 从URL获取图片数据
            with urllib.request.urlopen(image_url) as response:
                image_data = response.read()
            
            # 将图片数据加载到PIL Image
            img = Image.open(BytesIO(image_data))
            
            # 调整图片大小（如果需要）
            max_width, max_height = 300, 300
            img.thumbnail((max_width, max_height))
            
            # 转换为Tkinter可用的图片格式
            photo = ImageTk.PhotoImage(img)
            
            # 显示图片
            image_label = ttk.Label(scrollable_frame, image=photo)
            image_label.image = photo  # 保持引用以防止被垃圾回收
            image_label.pack(padx=20, pady=10)
        except Exception as e:
            logger.error(f"加载图片失败: {e}")
            ttk.Label(scrollable_frame, text="无法加载打赏图片", font=self.font, foreground="red").pack(padx=20, pady=10)
        
        # 创建关闭按钮
        btn_frame = ttk.Frame(scrollable_frame)
        btn_frame.pack(fill=tk.X, padx=15, pady=20)
        
        close_button = ttk.Button(btn_frame, text="关闭", command=dialog.destroy, width=15)
        close_button.pack(side=tk.RIGHT, padx=10, pady=5)
        
        # 确保对话框显示在最前面
        dialog.lift()
        dialog.attributes('-topmost', True)
        dialog.after_idle(dialog.attributes, '-topmost', False)

    def _create_result_tree_context_menu(self, result_tree):
        """为结果树创建右键菜单"""
        # 创建右键菜单
        self.result_tree_context_menu = tk.Menu(self.root, tearoff=0)
        
        # 添加复制功能
        self.result_tree_context_menu.add_command(label="复制选中单元格", command=lambda: self._copy_selected_cells(result_tree))
        self.result_tree_context_menu.add_command(label="复制当前单元格", command=lambda: self._copy_current_cell(result_tree))
        self.result_tree_context_menu.add_command(label="复制当前列", command=lambda: self._copy_current_column(result_tree))
        self.result_tree_context_menu.add_separator()
        self.result_tree_context_menu.add_command(label="全选", command=lambda: self._select_all(result_tree))
        
        # 绑定右键菜单显示事件
        result_tree.bind("<Button-3>", lambda event, tree=result_tree: self._show_result_tree_context_menu(event, tree))
        
        # 保存当前活动的结果树引用
        self.active_result_tree = result_tree
    
    def _show_result_tree_context_menu(self, event, result_tree):
        """显示结果树的右键菜单"""
        # 更新活动结果树引用
        self.active_result_tree = result_tree
        
        # 保存点击位置信息
        self.right_click_row = result_tree.identify_row(event.y)
        self.right_click_column = result_tree.identify_column(event.x)
        
        # 选中点击的行
        if self.right_click_row:
            result_tree.selection_set(self.right_click_row)
        
        # 显示右键菜单
        self.result_tree_context_menu.post(event.x_root, event.y_root)
    
    def _copy_selected_cells(self, result_tree):
        """复制选中的单元格内容"""
        selected_items = result_tree.selection()
        if not selected_items:
            return
        
        # 获取所有列
        columns = result_tree['columns']
        if not columns:
            return
        
        # 收集选中行的数据
        data = []
        for item in selected_items:
            values = result_tree.item(item, 'values')
            if values:
                data.append([str(val) if val is not None else '' for val in values])
        
        # 转换为制表符分隔的文本
        text_to_copy = '\n'.join(['\t'.join(row) for row in data])
        
        # 复制到剪贴板
        self.root.clipboard_clear()
        self.root.clipboard_append(text_to_copy)
        logger.info("已复制选中单元格内容到剪贴板")
    
    def _copy_current_cell(self, result_tree):
        """复制当前单元格内容"""
        if not hasattr(self, 'right_click_row') or not hasattr(self, 'right_click_column'):
            # 如果没有右键点击信息，使用当前选中的第一项
            selected_items = result_tree.selection()
            if not selected_items:
                return
            self.right_click_row = selected_items[0]
            # 默认使用第一列
            if result_tree['columns']:
                self.right_click_column = '#1'
            else:
                return
        
        if self.right_click_row and self.right_click_column:
            # 获取列索引
            try:
                col_index = int(self.right_click_column[1:]) - 1  # 转换为0-based索引
            except ValueError:
                return
            
            # 获取单元格值
            item = self.right_click_row
            values = result_tree.item(item, 'values')
            if values and 0 <= col_index < len(values):
                cell_value = str(values[col_index]) if values[col_index] is not None else ''
                
                # 复制到剪贴板
                self.root.clipboard_clear()
                self.root.clipboard_append(cell_value)
                logger.info("已复制当前单元格内容到剪贴板")
    
    def _copy_current_column(self, result_tree):
        """复制当前列内容"""
        if not hasattr(self, 'right_click_row') or not hasattr(self, 'right_click_column'):
            # 如果没有右键点击信息，使用当前选中的第一项和第一列
            selected_items = result_tree.selection()
            if not selected_items:
                return
            self.right_click_row = selected_items[0]
            # 默认使用第一列
            if result_tree['columns']:
                self.right_click_column = '#1'
            else:
                return
        
        if self.right_click_column:
            # 获取列索引
            try:
                col_index = int(self.right_click_column[1:]) - 1  # 转换为0-based索引
            except ValueError:
                return
            
            # 收集该列的所有数据
            column_data = []
            for item in result_tree.get_children():
                values = result_tree.item(item, 'values')
                if values and 0 <= col_index < len(values):
                    column_data.append(str(values[col_index]) if values[col_index] is not None else '')
            
            # 转换为换行分隔的文本
            text_to_copy = '\n'.join(column_data)
            
            # 复制到剪贴板
            self.root.clipboard_clear()
            self.root.clipboard_append(text_to_copy)
            logger.info("已复制当前列内容到剪贴板")
    
    def _select_all(self, result_tree):
        """全选结果树中的所有项"""
        all_items = result_tree.get_children()
        if all_items:
            result_tree.selection_set(all_items)
            logger.info("已全选查询结果")
    
    def _on_copy_shortcut(self, event=None):
        """处理Ctrl+C快捷键，复制当前选中内容"""
        if self.active_result_tree:
            self._copy_selected_cells(self.active_result_tree)
        app_controller.check_for_updates(callback=self._on_update_check_complete)
        
    def _create_query_editor_context_menu(self, query_editor):
        """为查询编辑器创建右键菜单"""
        # 创建右键菜单
        self.query_editor_context_menu = tk.Menu(self.root, tearoff=0)
        
        # 添加格式化SQL功能
        self.query_editor_context_menu.add_command(label="格式化SQL", command=lambda: self._format_sql(query_editor))
        self.query_editor_context_menu.add_command(label="清空", command=lambda: self._clear_text(query_editor))
        self.query_editor_context_menu.add_separator()
        
        # 添加基本编辑功能
        self.query_editor_context_menu.add_command(label="全选", command=lambda: self._select_all_text(query_editor))
        self.query_editor_context_menu.add_command(label="复制", command=lambda: self._copy_text(query_editor))
        self.query_editor_context_menu.add_command(label="粘贴", command=lambda: self._paste_text(query_editor))
        
        # 绑定右键菜单显示事件
        query_editor.bind("<Button-3>", lambda event: self._show_query_editor_context_menu(event, query_editor))
        
    def _show_query_editor_context_menu(self, event, query_editor):
        """显示查询编辑器的右键菜单"""
        # 保存当前活动的查询编辑器引用
        self.active_query_editor = query_editor
        
        # 显示右键菜单
        self.query_editor_context_menu.post(event.x_root, event.y_root)
        
    def _select_all_text(self, query_editor):
        """全选查询编辑器中的文本"""
        query_editor.tag_add(tk.SEL, "1.0", tk.END)
        query_editor.mark_set(tk.INSERT, "1.0")
        query_editor.see(tk.INSERT)
        
    def _copy_text(self, query_editor):
        """复制选中的文本"""
        try:
            selected_text = query_editor.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.root.clipboard_clear()
            self.root.clipboard_append(selected_text)
        except tk.TclError:
            # 没有选中的文本
            pass
            
    def _paste_text(self, query_editor):
        """粘贴文本"""
        try:
            clipboard_text = self.root.clipboard_get()
            # 检查是否有选中的文本
            try:
                query_editor.delete(tk.SEL_FIRST, tk.SEL_LAST)
            except tk.TclError:
                # 没有选中的文本，不做处理
                pass
            query_editor.insert(tk.INSERT, clipboard_text)
        except tk.TclError:
            # 剪贴板为空或有其他错误
            pass
            
    def _clear_text(self, query_editor):
        """清空查询编辑器中的文本"""
        query_editor.delete("1.0", tk.END)
        
    def _on_query_text_change(self, event, query_editor, combined_tab):
        """处理查询文本变更事件，更新标签页标题和语法高亮"""
        # 更新标签页标题
        self._update_query_tab_title(combined_tab)
        
        # 应用语法高亮
        self._apply_sql_syntax_highlighting(query_editor)
        
    def _apply_sql_syntax_highlighting(self, query_editor):
        """为查询编辑器应用SQL语法高亮，将保留字显示为蓝色，引号内字符串显示为绿色"""
        # 移除所有现有的高亮标签
        query_editor.tag_remove("keyword", "1.0", tk.END)
        query_editor.tag_remove("string", "1.0", tk.END)
        
        # 获取全部文本
        text = query_editor.get("1.0", tk.END)
        
        # 首先处理引号内的字符串高亮
        in_string = False
        quote_char = None
        string_start = "1.0"
        
        # 遍历文本的每个字符来识别字符串
        pos = 0
        while pos < len(text):
            char = text[pos]
            
            # 检测字符串开始
            if not in_string and (char == '"' or char == "'"):
                in_string = True
                quote_char = char
                # 计算实际位置
                line, col = self._calculate_position(text, pos)
                string_start = f"{line}.{col}"
            # 检测字符串结束（非转义引号）
            elif in_string and char == quote_char and (pos == 0 or text[pos-1] != '\\'):
                in_string = False
                # 计算实际位置
                line, col = self._calculate_position(text, pos + 1)
                string_end = f"{line}.{col}"
                # 应用字符串高亮
                query_editor.tag_add("string", string_start, string_end)
            
            pos += 1
        
        # 然后处理关键字高亮
        # 按照空白字符分割文本
        words = []
        current_word = ""
        
        for char in text:
            if char.isalnum() or char == '_':
                current_word += char
            else:
                if current_word:
                    words.append(current_word)
                    current_word = ""
                if char.strip():
                    words.append(char)
        
        if current_word:
            words.append(current_word)
        
        # 重新扫描文本并应用高亮
        current_pos = "1.0"
        for word in words:
            # 查找当前单词的位置
            start_pos = query_editor.search(word, current_pos, stopindex=tk.END)
            if not start_pos:
                break
            
            # 计算结束位置
            line, col = map(int, start_pos.split('.'))
            end_col = col + len(word)
            end_pos = f"{line}.{end_col}"
            
            # 检查是否为关键字（不区分大小写）
            if word.upper() in self.sql_keywords:
                query_editor.tag_add("keyword", start_pos, end_pos)
            
            # 更新当前位置
            current_pos = end_pos
    
    def _calculate_position(self, text, char_index):
        """根据字符索引计算文本位置（行，列）"""
        lines = text[:char_index].split('\n')
        line = len(lines)
        col = len(lines[-1])
        return (line, col)
            
    def _format_sql(self, query_editor):
        """格式化SQL语句"""
        logger.info("用户点击格式化SQL")
        
        try:
            # 获取当前选中的文本或全部文本
            try:
                sql_text = query_editor.get(tk.SEL_FIRST, tk.SEL_LAST)
                has_selection = True
            except tk.TclError:
                # 没有选中的文本，获取全部文本
                sql_text = query_editor.get("1.0", tk.END)
                has_selection = False
            
            # 如果文本为空，不做处理
            if not sql_text.strip():
                return
            
            # 简单的SQL格式化逻辑
            formatted_sql = self._simple_sql_formatter(sql_text)
            
            # 更新查询编辑器的内容
            if has_selection:
                query_editor.delete(tk.SEL_FIRST, tk.SEL_LAST)
                query_editor.insert(tk.SEL_FIRST, formatted_sql)
            else:
                query_editor.delete("1.0", tk.END)
                query_editor.insert("1.0", formatted_sql)
                
        except Exception as e:
            logger.error(f"格式化SQL失败: {str(e)}")
            messagebox.showerror("错误", f"格式化SQL失败:\n{str(e)}")
            
    def _simple_sql_formatter(self, sql_text):
        """简单的SQL格式化实现"""
        # 将SQL语句转换为大写并添加缩进
        keywords = ['SELECT', 'FROM', 'WHERE', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON', 'AND', 'OR', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'SET', 'DELETE', 'CREATE', 'TABLE', 'ALTER', 'DROP', 'GROUP BY', 'ORDER BY', 'LIMIT', 'OFFSET', 'HAVING']
        
        # 将SQL转换为大写进行关键词识别
        sql_upper = sql_text.upper()
        
        # 保存原始字符和大小写信息的映射
        char_map = list(enumerate(sql_text))
        
        # 查找所有关键词的位置
        positions = []
        for keyword in keywords:
            pos = 0
            while True:
                pos = sql_upper.find(keyword, pos)
                if pos == -1:
                    break
                
                # 确保关键词是独立的（前后不是字母或数字）
                prev_char = sql_upper[pos-1] if pos > 0 else ' '
                next_char = sql_upper[pos+len(keyword)] if (pos+len(keyword)) < len(sql_upper) else ' '
                if (not prev_char.isalnum()) and (not next_char.isalnum()):
                    positions.append((pos, pos+len(keyword), keyword))
                
                pos += 1
        
        # 按位置排序
        positions.sort(key=lambda x: x[0])
        
        # 格式化SQL，添加换行和缩进
        result = []
        last_pos = 0
        indent_level = 0
        
        for pos_start, pos_end, keyword in positions:
            # 添加关键词前的文本
            if pos_start > last_pos:
                result.append(sql_text[last_pos:pos_start])
            
            # 处理关键词前的换行和缩进
            if keyword in ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP']:
                result.append('\n')
                result.append('  ' * indent_level)
            elif keyword in ['FROM', 'WHERE', 'GROUP BY', 'ORDER BY', 'HAVING', 'LIMIT']:
                result.append('\n')
                result.append('  ' * indent_level)
                if keyword == 'WHERE':
                    indent_level += 1
            elif keyword in ['JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'OUTER JOIN']:
                result.append('\n')
                result.append('  ' * (indent_level + 1))
            elif keyword in ['AND', 'OR'] and sql_upper[pos_start-1:pos_start].strip() == '':
                result.append('\n')
                result.append('  ' * (indent_level + 1))
            
            # 添加关键词（保持原始大小写）
            result.append(sql_text[pos_start:pos_end])
            
            last_pos = pos_end
            
        # 添加剩余的文本
        if last_pos < len(sql_text):
            result.append(sql_text[last_pos:])
            
        # 处理括号的缩进
        formatted = ''.join(result)
        lines = formatted.split('\n')
        
        final_result = []
        current_indent = 0
        indent_size = 2
        
        for line in lines:
            stripped_line = line.strip()
            if not stripped_line:
                continue
            
            # 处理右括号的缩进
            if stripped_line.startswith(')'):
                current_indent = max(0, current_indent - indent_size)
            
            # 添加缩进和行内容
            final_result.append(' ' * current_indent + stripped_line)
            
            # 处理左括号的缩进
            open_brackets = stripped_line.count('(') - stripped_line.count(')')
            current_indent += open_brackets * indent_size
            current_indent = max(0, current_indent)
            
        # 合并所有行
        return '\n'.join(final_result).strip()
        
    def _on_update_check_complete(self, success, message, current_version, latest_version):
        """更新检查完成后的回调函数"""
        if success:
            if latest_version > current_version:
                # 如果有新版本可用，提供下载选项
                response = messagebox.askyesno(
                    "更新检查", 
                    f"发现新版本 v{latest_version}!\n当前版本 v{current_version}\n\n是否下载新版本？"
                )
                if response:
                    # 开始下载新版本
                    self.status_var.set("正在下载新版本...")
                    self._download_new_version(latest_version)
            else:
                # 当前已是最新版本
                messagebox.showinfo(
                    "更新检查", 
                    f"当前版本已经是最新版本 v{current_version}\n无需更新。"
                )
        else:
            # 显示错误消息
            if "无法连接" in message or "连接错误" in message:
                messagebox.showerror("更新检查失败", "网络不通，请检查网络连接或稍后再试")
            elif "超时" in message:
                messagebox.showerror("更新检查失败", "连接超时，请稍后再试")
            else:
                messagebox.showerror("更新检查失败", message)
        
        self.status_var.set("更新检查完成")
        
    def _download_new_version(self, latest_version):
        """下载新版本程序"""
        import threading
        import webbrowser
        
        def download():
            try:
                # 这里我们使用浏览器打开下载链接
                # 实际应用中可能需要实现直接下载
                download_url = "http://localhost:8080/api/update/download"
                self.status_var.set(f"正在打开下载链接...")
                webbrowser.open(download_url)
                self.status_var.set(f"请在浏览器中下载新版本 v{latest_version}")
                messagebox.showinfo("下载提示", "浏览器已打开下载链接，请按照提示完成下载。")
            except Exception as e:
                logger.error(f"下载新版本异常: {e}")
                self.status_var.set("下载失败")
                messagebox.showerror("下载失败", f"无法打开下载链接：{str(e)}")
        
        # 在新线程中执行下载，避免阻塞UI
        thread = threading.Thread(target=download)
        thread.daemon = True
        thread.start()

def run_gui():
    """运行GUI界面"""
    logger.info("GUI界面启动")
    try:
        root = tk.Tk()
        
        # 确保窗口标题设置正确
        root.title("mysqlpass - 自由使用的MySql工具!!  Freedam")
        
        # 强制设置中文字体支持
        default_font = tkfont.nametofont("TkDefaultFont")
        default_font.configure(family="Microsoft YaHei", size=10)
        text_font = tkfont.nametofont("TkTextFont")
        text_font.configure(family="Microsoft YaHei", size=10)
        
        # 获取屏幕的宽度和高度
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        
        # 使用固定的窗口大小，更容易调试显示问题
        window_width = 900
        window_height = 600
        
        # 确保窗口大小不超过屏幕大小
        window_width = min(window_width, screen_width - 100)
        window_height = min(window_height, screen_height - 100)
        
        # 设置窗口大小和位置（居中显示）
        x = (screen_width // 2) - (window_width // 2)
        y = (screen_height // 2) - (window_height // 2)
        root.geometry(f"{window_width}x{window_height}+{x}+{y}")
        
        # 记录窗口设置信息，帮助调试
        logger.info(f"窗口设置: 大小={window_width}x{window_height}, 位置={x},{y}")
        logger.info(f"屏幕大小: {screen_width}x{screen_height}")
        
        # 确保窗口可见性的关键设置
        root.deiconify()  # 确保窗口不被隐藏
        root.attributes('-alpha', 1.0)  # 确保窗口不透明
        
        # 确保窗口显示在前面
        root.attributes('-topmost', True)
        root.update_idletasks()
        root.attributes('-topmost', False)  # 取消置顶，但已经显示在前面了
        
        # 强制更新窗口
        root.update()
        
        # 创建应用实例
        app = MySQLPassGUI(root)
        
        logger.info("GUI应用实例已创建，进入主循环")
        root.mainloop()
        logger.info("GUI主循环已退出")
    except Exception as e:
        logger.error(f"GUI界面运行异常: {e}")
        # 尝试显示错误消息
        try:
            error_root = tk.Tk()
            error_root.withdraw()  # 隐藏主窗口
            error_root.attributes('-topmost', True)  # 确保错误窗口显示在前面
            messagebox.showerror("GUI错误", f"GUI界面运行异常: {str(e)}")
        except:
            pass
        raise

if __name__ == "__main__":
    run_gui()
